package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.injector.methods.Delete;
import com.baomidou.mybatisplus.core.injector.methods.DeleteById;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.common.constants.message.NewsAutoScanConstants;
import com.heima.common.constants.message.NewsUpOrDownConstants;
import com.heima.common.constants.message.PublishArticleConstants;
import com.heima.common.constants.wemedia.WemediaConstants;
import com.heima.common.exception.CustException;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.threadlocal.WmThreadLocalUtils;
import com.heima.model.wemedia.dtos.NewsAuthDTO;
import com.heima.model.wemedia.dtos.WmNewsDTO;
import com.heima.model.wemedia.dtos.WmNewsPageReqDTO;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmNewsMaterial;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.model.wemedia.vos.WmNewsVO;
import com.heima.wemedia.controller.v1.WmNewsController;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmNewsMaterialMapper;
import com.heima.wemedia.mapper.WmUserMapper;
import com.heima.wemedia.service.WmNewsService;
import com.sun.imageio.plugins.wbmp.WBMPImageReader;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper,WmNews> implements WmNewsService {

    /**
     * 注入oss的前缀路径
     */
    @Value("${file.oss.web-site}")
    private String webSite;


    @Autowired
    private RabbitTemplate rabbitTemplate;



    /**
     * 注入关联表的map对象
     */
    @Autowired
    private WmNewsMaterialMapper wmNewsMaterialMapper;


    /**
     * 注入素材表的mapper对象
     */
    @Autowired
    private WmMaterialMapper wmMaterialMapper;


    @Autowired
    private  WmNewsMapper wmNewsMapper;

    @Autowired
    private WmUserMapper wmUserMapper;


    /**
     * 分页查询
     * @param dto
     * @return
     */
    @Override
    public PageResponseResult findList(WmNewsPageReqDTO dto) {
        //校验参数
        dto.checkParam();
        WmUser user = WmThreadLocalUtils.getUser();
        if (user == null){
            CustException.cust(AppHttpCodeEnum.NEED_LOGIN,"亲先登录");
        }
        //封装查询条件
        LambdaQueryWrapper<WmNews> wrapper = new LambdaQueryWrapper<>();
        //标题模糊查询  判断用户传递的标题不为null
        if (StringUtils.isNotBlank(dto.getKeyword())){
            wrapper.like(WmNews::getTitle,dto.getKeyword());
        }
        //判断频道id
        if (dto.getChannelId() != null){
            wrapper.eq(WmNews::getChannelId,dto.getChannelId());
        }
        //判断状态
        if (dto.getStatus() != null){
            wrapper.eq(WmNews::getStatus,dto.getStatus());
        }
        //发布时间  开始和结束时间区间查询
        if (dto.getBeginPubDate() != null && dto.getEndPubDate() != null){
            wrapper.between(WmNews::getPublishTime,dto.getBeginPubDate(),dto.getEndPubDate());
        }
        //用户id
        wrapper.eq(WmNews::getUserId,user.getId());
        //创建时间 降序
        wrapper.orderByDesc(WmNews::getCreatedTime);


        //分页查询
        Page<WmNews> page = new Page<>(dto.getPage(), dto.getSize());
        IPage<WmNews> pageResult = this.page(page, wrapper);

        //封装结果
        PageResponseResult pageResponseResult = new PageResponseResult(dto.getPage(), dto.getSize(), pageResult.getTotal(), pageResult.getRecords());

        //处理图片
        pageResponseResult.setHost(webSite);

        //返回结果
        return pageResponseResult;
    }




    /**
     * 作者发布文章
     * @param dto
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResult submitNews(WmNewsDTO dto) {
        //判断用户登录
        if (WmThreadLocalUtils.getUser() == null){
            CustException.cust(AppHttpCodeEnum.NEED_LOGIN);
        }
        //校验参数
        //文章内容
        if (StringUtils.isBlank(dto.getContent())){
            CustException.cust(AppHttpCodeEnum.PARAM_REQUIRE,"文章缺少内容");
        }
        //将文章接收类的数据转化到文章对象中 存储到数据库
        WmNews wmNews = new WmNews();
        BeanUtils.copyProperties(dto,wmNews);
        //如果文章的格式是自动模式 就需要江type设置成null
        if (WemediaConstants.WM_NEWS_TYPE_AUTO.equals(dto.getType())){
            wmNews.setType(null);
        }

        //处理dto参数  将封面的集合转化成字符串
        //调用方法  将前段传递过来的封面图片  去除前缀    拼接成字符串  图片路径之间用逗号隔开
        String images = imageListToStr(dto.getImages());
        //将封面图片字符串封装到对象中
        wmNews.setImages(images);
        //调用保存文件的方法
        saveWmNews(wmNews);

        //判断是否是草稿
        if (WemediaConstants.WM_NEWS_DRAFT_STATUS.equals(wmNews.getStatus())) {
            return ResponseResult.okResult();
        }



        //  抽取文章中关联的图片路径
        // 将正文部分的内容反json化
        //  json格式的数据       转化成list集合     数据          list集合中的元素的字节码对象
        List<Map> contents = JSON.parseArray(dto.getContent(), Map.class);
        //调用方法  将正文内容的数据处理成  图片集合
        List<String> newsImageList = parseContentImages(contents);



        //   关联文章中的图片和素材的关系   将文章的id  和 文章需要的图片的id  插入到中间表
        //判断素材集合  集合数据不为null
        if (CollectionUtils.isNotEmpty(contents)){
            // 调用方法      图片素材集合        文章的id          引用类型  0->代表文章引用  1->封面引用
            saveRelativeInfo(newsImageList , wmNews.getId() , WemediaConstants.WM_CONTENT_REFERENCE);
        }

        //   关联文章封面和素材的关系
        saveRelativeInfoForCover(newsImageList, wmNews , dto);

        //发送审核的消息到队列
        rabbitTemplate.convertAndSend(NewsAutoScanConstants.WM_NEWS_AUTO_SCAN_QUEUE, wmNews.getId());

        return ResponseResult.okResult();
    }




    private void saveRelativeInfoForCover(List<String> contentUrls, WmNews wmNews,WmNewsDTO dto) {
        // 1. 前端传入的封面集合   （有前缀）
        List<String> images = dto.getImages();
        // 2. 判断dto type是否为 -1   ，需要生成封面
        if (WemediaConstants.WM_NEWS_TYPE_AUTO.equals(dto.getType())){
            // 说明内容无图片
            if(CollectionUtils.isEmpty(contentUrls)){
                wmNews.setType(WemediaConstants.WM_NEWS_NONE_IMAGE);
            }else if(contentUrls.size()>0 && contentUrls.size() <= 2){
                wmNews.setType(WemediaConstants.WM_NEWS_SINGLE_IMAGE);
                images = contentUrls.stream().limit(1).collect(Collectors.toList());
            }else {
                wmNews.setType(WemediaConstants.WM_NEWS_MANY_IMAGE);
                images = contentUrls.stream().limit(3).collect(Collectors.toList());
            }

            if(CollectionUtils.isNotEmpty(images)){
                wmNews.setImages(imageListToStr(images));
            }
            // 修改wmNews
            updateById(wmNews);
        }
        if(CollectionUtils.isNotEmpty(images)){
            images = images.stream().map(url -> url.replaceAll(webSite,""))
                    .distinct().collect(Collectors.toList());
            // 3. 保存封面和素材的关联关系
            saveRelativeInfo(images,wmNews.getId(),WemediaConstants.WM_IMAGE_REFERENCE);
        }

    }




    /**
     *  保存文章中的图片和  和 素材的关联关系
     * @param newsImageList  素材路径结婚
     * @param id   文章id
     * @param wmContentReference   内容
     */
    private void saveRelativeInfo(List<String> newsImageList, Integer id, Short wmContentReference) {
        //获取当前的登录用户
        WmUser user = WmThreadLocalUtils.getUser();
        if (user == null){
            CustException.cust(AppHttpCodeEnum.NEED_LOGIN);
        }
        //根据图片路径列表  和 登录的用户id  查询相关的素材id
        //掉用方法   方法根据图片的路径 和 用户的id  在素材的表中查询对应的图片的id  返回集合
        // 文章中的图片不为null的时候  才在中间表插入数据
        if (CollectionUtils.isNotEmpty(newsImageList)){
            List<Integer> materiaIdList = wmMaterialMapper.selectRelationsIds(newsImageList, user.getId());
            //检查id的数量  是否少于集合中的图片的数量  如果少了 代表素材缺少
            if (CollectionUtils.isEmpty(materiaIdList) || materiaIdList.size() < newsImageList.size()){
                CustException.cust(AppHttpCodeEnum.PARAM_REQUIRE,"缺少素材数据");
            }

            //将图片id 和  用户id  插入到中间表
            wmNewsMaterialMapper.saveRelations(materiaIdList,id,wmContentReference);
        }
    }




    /**
     * 将正文的图片抽取出来
     * @param contents
     * @return
     */
    private List<String> parseContentImages(List<Map> contents) {
        //获取stream流
        return contents.stream()
                //过滤    获取map集合中的key  type  对应的 value  为image的数据
                .filter(map -> map.get("type").equals(WemediaConstants.WM_NEWS_TYPE_IMAGE))
                //    获取 key 为 image  的对应的value中的数据   转化成字符串
                // 获取出了每一个图片的全路径名
                .map(x -> (String)x.get("value"))
                //去除前缀
                .map(image -> image.replaceAll(webSite , ""))
                //去重
                .distinct()
                //将流结束  数据转成list集合
                .collect(Collectors.toList());

    }




    /**
     * 判断文件是修改还是保存
     * @param wmNews
     */
    private void saveWmNews(WmNews wmNews) {
        //添加字段的值
        wmNews.setSubmitedTime(new Date());
        wmNews.setPublishTime(new Date());
        wmNews.setUserId(WmThreadLocalUtils.getUser().getId());
        //文章发布默认上架
        wmNews.setEnable(WemediaConstants.WM_NEWS_UP);

        //判断文章是保存还是修改
        //没有id就是保存
        if (wmNews.getId() == null){
            //保存
            save(wmNews);
        }else {
            //  有 id  修改文章
            //先删除改文章有关系的关联表
            LambdaQueryWrapper<WmNewsMaterial> wrapper = Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId, wmNews.getId());
            //删除中间表
            wmNewsMaterialMapper.delete(wrapper);
            //修改数据
            updateById(wmNews);
        }
    }




    /**
     * 将制前段传递过来的封面集合转化成字符串
     * @param images
     * @return
     */
    private String imageListToStr(List<String> images ) {
        //使用stream流
        return images.stream()
                //映射  取出其中一个元素所做的操作 其他的所有元素同步这个操作
                //                  替换所有    路径前缀        空字符串
                .map(image -> image.replaceAll(webSite,""))
                // 结束流           将元素连接      用  ,
                .collect(Collectors.joining(","))
                ;
    }



    /**
     * 根据id查询单篇文章
     * @param id
     * @return
     */
    @Override
    public ResponseResult findNewById(Integer id) {
        //判断参数
        if (id == null){
            CustException.cust(AppHttpCodeEnum.PARAM_REQUIRE);
        }
//        WmUser user = WmThreadLocalUtils.getUser();
//        if (user == null){
//            CustException.cust(AppHttpCodeEnum.NEED_LOGIN);
//        }
//        //根据id查询文章信息
//        WmNews wmNews = this.getById(id);
//        //给每张图片加上前缀
//        //获取图片字符串
//        String images = wmNews.getImages();
//        String[] strings = images.split(",");
//        String collect = Arrays.stream(strings).map(url -> webSite + url).collect(Collectors.joining(","));
//
//
//        wmNews.setImages(collect);
//        String[] imageArray = images.split(",");
//        //获取每一个图片路径
//        ArrayList<String> arrayList = new ArrayList<>();
//        for (String url : imageArray) {
//            url = webSite+url;
//        }
//        //转化成字符串
//        String imageAll = imageArray.toString();

        WmNews wmNews = this.getOne(Wrappers.<WmNews>lambdaQuery().eq(WmNews::getId, id));
        if (wmNews == null){
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST);
        }


        //返回数据
        ResponseResult responseResult = ResponseResult.okResult(wmNews);
        responseResult.setHost(webSite);
        return responseResult;
    }


    /**
     * 根据id删除文章
     * @param id
     * @return
     */
    @Override
    public ResponseResult deleteNewById(Integer id) {
        //判断参数
        if (id == null){
            CustException.cust(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        //根据id获取文章对象
        WmNews wmNews = getById(id);
        if (wmNews==null){
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST,"文章不存在");
        }
        //判断状态
        if (WemediaConstants.WM_NEWS_PUBLISH_STATUS.equals(wmNews.getStatus()) && WemediaConstants.WM_NEWS_UP.equals(wmNews.getEnable())){
            //文章状态为9 并且在上架状态  则不能删除
            CustException.cust(AppHttpCodeEnum.DATA_NOT_ALLOW,"改文章上架中无法删除");
        }

        //查询改文章的关联表
        LambdaQueryWrapper<WmNewsMaterial> wrapper = Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId, wmNews.getId());
        List<WmNewsMaterial> wmNewsMaterials = wmNewsMaterialMapper.selectList(wrapper);
        //如果关联表的数据不为空 这删除关联表  (只能删除文章图片联系)
        if (CollectionUtils.isNotEmpty(wmNewsMaterials)){
            for (WmNewsMaterial material : wmNewsMaterials) {
                wmNewsMaterialMapper.deleteById(material.getId());
            }
        }
        //删除文章
        removeById(wmNews.getId());
        //返回结果
        return ResponseResult.okResult();
    }


    /**
     * 文章上下架
     * @param dto
     * @return
     */
    @Override
    public ResponseResult downOrUp(WmNewsDTO dto) {
        //检查参数
        if (dto.getId() == null || dto.getEnable() == null) {
            CustException.cust(AppHttpCodeEnum.PARAM_REQUIRE, "参数异常");
        }
        //获取自媒体文章
        WmNews wmNews = this.getOne(Wrappers.<WmNews>lambdaQuery().eq(WmNews::getId, dto.getId()));
        //判断自媒体文章是否存在
        if (wmNews == null) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST, "文章不存在");
        }
        //判断自媒体文章状态是否已发布
        if (!WemediaConstants.WM_NEWS_PUBLISH_STATUS.equals(wmNews.getStatus())) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_ALLOW, "未发布的文章不能上下架");
        }
        Short enable = dto.getEnable();
        wmNews.setEnable(enable);
        this.updateById(wmNews);
        //判断文章id是否存在,上下架的文章一般都有id
        if (wmNews.getArticleId()!=null) {
            //上架
            if (WemediaConstants.WM_NEWS_UP.equals(enable)) {
                //发送消息到上架消息队列,消息内容是文章id
                rabbitTemplate.convertAndSend(NewsUpOrDownConstants.NEWS_UP_OR_DOWN_EXCHANGE, NewsUpOrDownConstants.NEWS_UP_ROUTE_KEY, wmNews.getArticleId());
            } else {//下架
                //发送消息到下架消息队列,消息内容是文章id
                rabbitTemplate.convertAndSend(NewsUpOrDownConstants.NEWS_UP_OR_DOWN_EXCHANGE, NewsUpOrDownConstants.NEWS_DOWN_ROUTE_KEY, wmNews.getArticleId());
            }
        }
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }


    /**
     * 查询文章列表
     * @param dto
     * @return
     */
    @Override
    public ResponseResult findNews(NewsAuthDTO dto) {
        //1.检查参数
        dto.checkParam();
        //记录当前页
        int currentPage = dto.getPage();
        //设置起始页
        dto.setPage((dto.getPage()-1)*dto.getSize());
        if(StringUtils.isNotBlank(dto.getTitle())){
            dto.setTitle("%"+dto.getTitle()+"%");
        }

        //2.分页查询
        List<WmNewsVO> wmNewsVoList = wmNewsMapper.findListAndPage(dto);
        //统计多少条数据
        long count = wmNewsMapper.findListCount(dto);

        //3.结果返回
        ResponseResult result = new PageResponseResult(currentPage, dto.getSize(), count, wmNewsVoList);
        result.setHost(webSite);
        return result;
    }


    /**
     * 根据id查询文章
     * @param id
     * @return
     */
    @Override
    public ResponseResult findWmNewsVo(Integer id) {
        //1参数检查
        if(id == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //2.查询文章信息
        WmNews wmNews = getById(id);
        if(wmNews == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //3.查询作者
        WmUser wmUser = null;
        if(wmNews.getUserId() != null){
            wmUser = wmUserMapper.selectById(wmNews.getUserId());
        }

        //4.封装vo信息返回
        WmNewsVO wmNewsVo = new WmNewsVO();
        BeanUtils.copyProperties(wmNews,wmNewsVo);
        if(wmUser != null){
            wmNewsVo.setAuthorName(wmUser.getName());
        }
        ResponseResult responseResult = ResponseResult.okResult(wmNewsVo);
        responseResult.setHost(webSite);
        return responseResult;
    }




    /**
     * 自媒体文章人工审核
     * @param status 2  审核失败  4 审核成功
     * @param dto
     * @return
     */
    @Override
    public ResponseResult updateStatus(Short status, NewsAuthDTO dto) {
        //1.参数检查
        if(dto == null || dto.getId() == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //2.查询文章
        WmNews wmNews = getById(dto.getId());
        if(wmNews == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        // 检查文章状态 不能为9  已发布
        if (wmNews.getStatus().equals(WmNews.Status.PUBLISHED.getCode())) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_ALLOW,"文章已发布");
        }
        //3.修改文章状态
        wmNews.setStatus(status);
        if(StringUtils.isNotBlank(dto.getMsg())){
            wmNews.setReason(dto.getMsg());
        }
        updateById(wmNews);

        // TODO 通知定时发布文章
        //文章准备就绪   将文章发送到队列   准备发布
        //发布时间
        long timeOut = wmNews.getPublishTime().getTime();
        //当前时间
        long timeNow = new Date().getTime();
        //时间差
        long time = timeOut - timeNow;

        //发布文章到交换机
        rabbitTemplate.convertAndSend(PublishArticleConstants.DELAY_DIRECT_EXCHANGE,  //交换价
                PublishArticleConstants.PUBLISH_ARTICLE_ROUTE_KEY,   //路由key
                wmNews.getId(),   //发送的内容
                (message -> {     //延迟队列的时间
                    message.getMessageProperties().setHeader("x-delay",time<= 0 ? 0 : time);
                    return message;
                }));
        log.info("立即发布文章通知成功发送，文章id : {}", wmNews.getId());

        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }
}



